package cn.bugstack.springframework.beans.factory;

import cn.bugstack.springframework.beans.BeansException;
import cn.bugstack.springframework.beans.PropertyValue;
import cn.bugstack.springframework.beans.PropertyValues;
import cn.bugstack.springframework.beans.factory.config.BeanDefinition;
import cn.bugstack.springframework.beans.factory.config.BeanFactoryPostProcessor;
import cn.bugstack.springframework.core.io.DefaultResourceLoader;
import cn.bugstack.springframework.core.io.Resource;
import cn.bugstack.springframework.util.StringValueResolver;

import java.io.IOException;
import java.util.Properties;

/**
 * PropertyPlaceholderConfigurer类用于从属性资源（例如属性文件）配置单个bean属性值。
 * 这对于使用面向系统管理员的自定义配置文件来覆盖应用程序上下文中配置的bean属性非常有用。
 */
public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor {

    // 默认占位符前缀
    public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";

    // 默认占位符后缀
    public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";

    // 属性文件的位置
    private String location;

    /**
     * 处理BeanFactory，替换bean定义中的占位符为实际属性值。
     * @param beanFactory 待处理的BeanFactory，不允许为null。
     * @throws BeansException 如果处理过程中发生错误。
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        try {
            // 加载属性文件
            DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
            Resource resource = resourceLoader.getResource(location);

            // 读取属性文件内容
            Properties properties = new Properties();
            properties.load(resource.getInputStream());

            // 遍历所有bean定义，替换占位符
            String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();

            for (String beanName : beanDefinitionNames) {

//                if (beanName.equals("userService")) {
//                    int bt = 0;
//                }

                BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);

                PropertyValues propertyValues = beanDefinition.getPropertyValues();

                for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
                    Object value = propertyValue.getValue();
                    if (!(value instanceof String)) {
                        continue;
                    }
                    // 解析占位符
                    value = resolvePlaceholder((String) value, properties);
                    // TODO 这里在添加新的PropertyValue后，并没有删除原来的PropertyValue
                    propertyValues.addPropertyValue(new PropertyValue(propertyValue.getName(), value));
                }
            }

            // 为容器添加字符串解析器，以支持@Value注解的解析
            StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(properties);
            // 该行代码的作用是将属性值写入AbstractBeanFactory类的emdeddedValueResolvers集合中
            beanFactory.addEmbeddedValueResolver(valueResolver);

        } catch (IOException e) {
            throw new BeansException("Could not load properties", e);
        }
    }

    /**
     * 解析字符串中的占位符，替换为实际的属性值。
     * @param value 包含占位符的字符串。
     * @param properties 属性文件的内容。
     * @return 替换占位符后的字符串。
     */
    private String resolvePlaceholder(String value, Properties properties) {
        String strVal = value;
        StringBuilder buffer = new StringBuilder(strVal);
        int startIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_PREFIX);
        int stopIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_SUFFIX);
        if (startIdx != -1 && stopIdx != -1 && startIdx < stopIdx) {
            String propKey = strVal.substring(startIdx + 2, stopIdx);
            String propVal = properties.getProperty(propKey);
            buffer.replace(startIdx, stopIdx + 1, propVal);
        }
        return buffer.toString();
    }

    /**
     * 设置属性文件的位置。
     * @param location 属性文件的路径。
     */
    public void setLocation(String location) {
        this.location = location;
    }

    /**
     * 内部类PlaceholderResolvingStringValueResolver实现了StringValueResolver接口，
     * 用于解析字符串中的占位符。
     *
     * Java 中的内部类可以访问其外部类（即包含它的类）的成员变量和方法，包括私有成员。
     * 这是因为内部类天生与其外部类有某种特殊的关联，使得内部类能够访问外部类的成员，而反之则不成立。
     *
     */
    private class PlaceholderResolvingStringValueResolver implements StringValueResolver {

        private final Properties properties; // 属性文件的内容

        /**
         * 构造函数。
         * @param properties 属性文件的内容。
         */
        public PlaceholderResolvingStringValueResolver(Properties properties) {
            this.properties = properties;
        }

        /**
         * 解析字符串中的占位符。
         * @param strVal 待解析的字符串。
         * @return 解析后的字符串。
         */
        @Override
        public String resolveStringValue(String strVal) {
            return PropertyPlaceholderConfigurer.this.resolvePlaceholder(strVal, properties);
        }

    }

}
